From 84e099b8f4669b65ebd6f4d2a03fffabde97ad35 Mon Sep 17 00:00:00 2001 From: "Karl O. Pinc" Date: Tue, 24 Nov 2020 16:07:02 -0600 Subject: [PATCH] Make routes for all components available to templates --- README.rst | 22 ++++ src/pgwui_common/pgwui_common.py | 51 +++++++-- tests/test_pgwui_common.py | 176 +++++++++++++++++++++++++------ 3 files changed, 204 insertions(+), 45 deletions(-) diff --git a/README.rst b/README.rst index 938b2ba..589842b 100644 --- a/README.rst +++ b/README.rst @@ -78,6 +78,28 @@ PGWUI modules all have the following configuration settings: The label for PGWUI_Menu to display, when different from the default +Common Template Variables +^^^^^^^^^^^^^^^^^^^^^^^^^ + +The `@base_view` decorator, and it's decendents, makes the following variables +available in templates: + + pgwui:: A dict, containing the following keys: + + routes:: A dict, keyed by PGWUI component name. Each value is the + URL used to reach the component. There are the following special + component names: + + pgwui_home:: The URL to the pgwui.home_page setting. This key + is always available. + + pgwui_menu:: The URL to the menu of PGWUI components. This + obtains its value from the ``pgwui.menu_page`` configuration + setting, if present. Otherwise it is the URL to the PGWUI_Menu + component, if the component is present. Otherwise the key + does not exist. + + Configuration By Python Code ---------------------------- diff --git a/src/pgwui_common/pgwui_common.py b/src/pgwui_common/pgwui_common.py index 22acaf7..cba4c90 100644 --- a/src/pgwui_common/pgwui_common.py +++ b/src/pgwui_common/pgwui_common.py @@ -22,9 +22,49 @@ '''Provide a way to configure PGWUI. ''' +from .plugin import find_pgwui_components + + DEFAULT_HOME_ROUTE = '/' +def set_menu_route(request, routes): + '''Add routes for pgwui_menu, return non-menu components + ''' + try: + menu_url = request.route_url('pgwui_menu') + except KeyError: + pass + else: + if menu_url != request.route_url('pgwui_home'): + routes['pgwui_menu'] = request.route_path('pgwui_menu') + + +def set_component_routes(request, routes): + '''Add routes for each pgwui component to the 'routes' dict + ''' + set_menu_route(request, routes) + components = find_pgwui_components() + if 'pgwui_menu' in components: + components.remove('pgwui_menu') + + for component in components: + try: + route = request.route_path(component) + except KeyError: + pass # In case a component has no route + else: + routes.setdefault(component, route) + + +def set_routes(request, routes): + '''Build 'routes' dict with all the routes + ''' + home_route = request.route_path('pgwui_home') + routes.setdefault('pgwui_home', home_route) + set_component_routes(request, routes) + + def base_view(wrapped): '''Decorator for any view which includes base.mk. ''' @@ -33,17 +73,8 @@ def base_view(wrapped): ''' response = wrapped(request) pgwui = response.get('pgwui', {}) - routes = pgwui.setdefault('routes', dict()) - routes.setdefault('pgwui_home', - request.route_url('pgwui_home')) - try: - logout_route = request.route_url('pgwui_logout') - except KeyError: - pass # A logout route is not required - else: - routes.setdefault('pgwui_logout', logout_route) - + set_routes(request, routes) response['pgwui'] = pgwui return response return wrapper diff --git a/tests/test_pgwui_common.py b/tests/test_pgwui_common.py index 73e7faa..d735ee0 100644 --- a/tests/test_pgwui_common.py +++ b/tests/test_pgwui_common.py @@ -35,6 +35,12 @@ pytest_plugins = ("pgwui",) FOO_URL = 'foo://bar/' +mock_find_pgwui_components = testing.make_mock_fixture( + pgwui_common, 'find_pgwui_components') + +mock_route_path = testing.instance_method_mock_fixture('route_path') +mock_route_url = testing.instance_method_mock_fixture('route_url') + def mock_view(request): return {'pgwui': {'foo': FOO_URL}} @@ -46,59 +52,131 @@ def check_base_view_results(request, pgwui): # Unit tests -# base_view() -def test_base_view_add(pyramid_request_config): - '''The response adds all expected variables''' - def mock_view(request): - return {} +# set_menu_route() - request = get_current_request() - url = (request.application_url - + pgwui_common.DEFAULT_HOME_ROUTE) - pgwui_common.includeme(pyramid_request_config) - wrapper = pgwui_common.base_view(mock_view) - response = wrapper(request) - assert response['pgwui']['routes']['pgwui_home'] == url +@pytest.mark.unittest +@pytest.mark.parametrize( + "test_routes,expected", + [ + # menu and home have identical routes, no route is added for menu + ({'pgwui_menu': '/', 'pgwui_home': '/'}, + {}), + # No menu route, no route is added for menu + ({'pgwui_home': '/'}, + {}), + # menu and home have different urls, route is added for menu + ({'pgwui_menu': '/menu', 'pgwui_home': '/'}, + {'pgwui_menu': '/menu'})]) +def test_set_menu_route( + pyramid_request_config, mock_route_path, mock_route_url, + test_routes, expected): + '''The expected routes are returned + ''' + def path_func(name): + return test_routes[name] + def url_func(name): + return f'{request.application_url}{test_routes[name]}' -def test_base_view_default(pyramid_request_config): - '''The response retains the mock view's variables''' - pgwui_common.includeme(pyramid_request_config) - wrapper = pgwui_common.base_view(mock_view) request = get_current_request() - response = wrapper(request) - pgwui = response['pgwui'] - check_base_view_results(request, pgwui) + mocked_route_path = mock_route_path(request) + mocked_route_path.side_effect = path_func + mocked_route_url = mock_route_url(request) + mocked_route_url.side_effect = url_func + routes = dict() + pgwui_common.set_menu_route(request, routes) -def test_base_view_logout(pyramid_request_config): - '''The response contains base_view variables - when there is a logout route + assert routes == expected + + +mock_set_menu_route = testing.make_mock_fixture( + pgwui_common, 'set_menu_route') + + +# set_component_routes() + +@pytest.mark.unittest +def test_set_component_routes( + pyramid_request_config, mock_route_path, mock_set_menu_route, + mock_find_pgwui_components): + '''Routes are set for every component which has a route, except for + pgwui_menu ''' - pgwui_common.includeme(pyramid_request_config) + test_routes = {'pgwui_menu': '/menu', + 'pgwui_logout': '/logout', + 'pgwui_foo': '/foo', + 'pgwui_home': '/'} + test_components = list(test_routes) + ['pgwui_noroute'] + + def route_func(route): + return test_routes[route] - logout_route = '/logout' - pyramid_request_config.add_route('pgwui_logout', logout_route) - wrapper = pgwui_common.base_view(mock_view) request = get_current_request() + mocked_route_path = mock_route_path(request) + mocked_route_path.side_effect = route_func + mock_find_pgwui_components.return_value = test_components - response = wrapper(request) - pgwui = response['pgwui'] - check_base_view_results(request, pgwui) - assert pgwui['routes']['pgwui_logout'] == (request.application_url - + logout_route) + routes = dict() + pgwui_common.set_component_routes(request, routes) + + expected_routes = test_routes.copy() + del expected_routes['pgwui_menu'] + + assert routes == expected_routes -def test_base_view_nologout(pyramid_request_config): - '''The response contains base_view and auth_base_view variables - when there is no logout route +mock_set_component_routes = testing.make_mock_fixture( + pgwui_common, 'set_component_routes') + + +# set_routes() + +@pytest.mark.unittest +def test_set_routes( + pyramid_request_config, mock_set_component_routes, mock_route_path): + '''The 'home' route is added and set_component_routes() called ''' + request = get_current_request() + + mocked_route_path = mock_route_path(request) + mocked_route_path.return_value = pgwui_common.DEFAULT_HOME_ROUTE + + routes = dict() + pgwui_common.set_routes(request, routes) + + assert routes['pgwui_home'] == pgwui_common.DEFAULT_HOME_ROUTE + mock_set_component_routes.assert_called_once() + + +mock_set_routes = testing.make_mock_fixture( + pgwui_common, 'set_routes') + + +# base_view() +@pytest.mark.unittest +def test_base_view_routes(pyramid_request_config, mock_set_routes): + '''The response has the 'pgwui['routes']' dict added to it''' + def mock_view(request): + return {} + pgwui_common.includeme(pyramid_request_config) + wrapper = pgwui_common.base_view(mock_view) + response = wrapper(get_current_request()) + assert 'pgwui' in response + pgwui = response['pgwui'] + assert 'routes' in pgwui + assert isinstance(pgwui['routes'], dict) + + +@pytest.mark.unittest +def test_base_view_default(pyramid_request_config): + '''The response retains the mock view's variables''' + pgwui_common.includeme(pyramid_request_config) wrapper = pgwui_common.base_view(mock_view) request = get_current_request() - response = wrapper(request) pgwui = response['pgwui'] check_base_view_results(request, pgwui) @@ -152,6 +230,34 @@ def test_includeme_configurecalled(): # Integration tests +# auth_base_view() + +@pytest.mark.integrationtest +def test_auth_base_view_integration( + pyramid_request_config, mock_find_pgwui_components): + '''There are routes for every component + ''' + test_routes = { + 'pgwui_menu': '/menu', + 'pgwui_logout': '/logout', + 'pgwui_foo': '/foo'} + + mock_find_pgwui_components.return_value = list(test_routes) + + pgwui_common.includeme(pyramid_request_config) + for name, route in test_routes.items(): + pyramid_request_config.add_route(name, route) + + wrapper = pgwui_common.auth_base_view(mock_view) + request = get_current_request() + result = wrapper(request) + + assert result['pgwui']['routes'] == dict(test_routes, pgwui_home='/') + + +# includeme() + +@pytest.mark.integrationtest def test_includeme(): config = pyramid.config.Configurator() pgwui_common.includeme(config) -- 2.34.1